/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.io;

import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * 主要是提供访问容器内资源的API
 * Provides a very simple API for accessing resources within an application server.
 *
 * @author Ben Gunter
 */
public abstract class VFS {

    private static final Log log = LogFactory.getLog(VFS.class);

    /**
     * 内置的VFS实现类
     */
    public static final Class<?>[] IMPLEMENTATIONS = {JBoss6VFS.class, DefaultVFS.class};

    /**
     * 用户自定义VFS实现集合。
     * <p>
     * 通过{@link #addImplClass(Class)}方法添加的用户自定义实现。
     */
    public static final List<Class<? extends VFS>> USER_IMPLEMENTATIONS = new ArrayList<>();

    /**
     * VFS 单例实例持有者
     */
    private static class VFSHolder {

        static final VFS INSTANCE = createVFS();

        @SuppressWarnings("unchecked")
        static VFS createVFS() {
            // Try the user implementations first, then the built-ins
            List<Class<? extends VFS>> impls = new ArrayList<>();
            // 优先使用用户自己加载的
            impls.addAll(USER_IMPLEMENTATIONS);
            // 使用系统默认的
            impls.addAll(Arrays.asList((Class<? extends VFS>[]) IMPLEMENTATIONS));

            // 遍历所有实现类，获取一个有效的
            VFS vfs = null;
            for (int i = 0; vfs == null || !vfs.isValid(); i++) {
                // 当获取不到vfs对象时或者找到有效的vfs对象时结束.

                // 获取vfs实例类型
                Class<? extends VFS> impl = impls.get(i);
                try {
                    // 实例化vfs
                    vfs = impl.newInstance();
                    if (vfs == null || !vfs.isValid()) {
                        if (log.isDebugEnabled()) {
                            log.debug("VFS implementation " + impl.getName() +
                                    " is not valid in this environment.");
                        }
                    }
                } catch (InstantiationException e) {
                    log.error("Failed to instantiate " + impl, e);
                    return null;
                } catch (IllegalAccessException e) {
                    log.error("Failed to instantiate " + impl, e);
                    return null;
                }
            }

            if (log.isDebugEnabled()) {
                log.debug("Using VFS adapter " + vfs.getClass().getName());
            }

            return vfs;
        }
    }

    /**
     * 获取VFS实现
     */
    public static VFS getInstance() {
        return VFSHolder.INSTANCE;
    }

    /**
     * 添加用户自定义的VFS实现，自定义实现优先级高于内置的实现
     *
     * @param clazz 被添加的{@link VFS}实现
     */
    public static void addImplClass(Class<? extends VFS> clazz) {
        if (clazz != null) {
            USER_IMPLEMENTATIONS.add(clazz);
        }
    }

    /**
     * 通过类名称加载类
     */
    protected static Class<?> getClass(String className) {
        try {
            return Thread.currentThread().getContextClassLoader().loadClass(className);
//      return ReflectUtil.findClass(className);
        } catch (ClassNotFoundException e) {
            if (log.isDebugEnabled()) {
                log.debug("Class not found: " + className);
            }
            return null;
        }
    }

    /**
     * 通过名称和入参类型获取方法
     *
     * @param clazz          定义了方法的类
     * @param methodName     方法名称
     * @param parameterTypes 方法入参类型集合
     */
    protected static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
        if (clazz == null) {
            return null;
        }
        try {
            return clazz.getMethod(methodName, parameterTypes);
        } catch (SecurityException e) {
            log.error("Security exception looking for method " + clazz.getName() + "." + methodName + ".  Cause: " + e);
            return null;
        } catch (NoSuchMethodException e) {
            log.error("Method not found " + clazz.getName() + "." + methodName + "." + methodName + ".  Cause: " + e);
            return null;
        }
    }

    /**
     * 执行方法并获取返回值
     *
     * @param method     需要执行的方法
     * @param object     方法所属对象实例
     * @param parameters 方法入参列表
     * @return 方法返回值
     * @throws IOException      If I/O errors occur
     * @throws RuntimeException If anything else goes wrong
     */
    @SuppressWarnings("unchecked")
    protected static <T> T invoke(Method method, Object object, Object... parameters)
            throws IOException, RuntimeException {
        try {
            return (T) method.invoke(object, parameters);
        } catch (IllegalArgumentException | IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            if (e.getTargetException() instanceof IOException) {
                throw (IOException) e.getTargetException();
            } else {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 获取指定路径下的{@link URL}列表
     *
     * @param path 资源路径
     * @return URL列表
     * @throws IOException If I/O errors occur
     */
    protected static List<URL> getResources(String path) throws IOException {
        return Collections.list(Thread.currentThread().getContextClassLoader().getResources(path));
    }

    /**
     * 在当前环境内，该VFS实现是否有效
     */
    public abstract boolean isValid();

    /**
     * 递归获取指定路径下的所有资源列表
     *
     * @param url     资源路径
     * @param forPath 用于获取URL的参数
     * @return 包含了子资源的列表
     * @throws IOException If I/O errors occur
     */
    protected abstract List<String> list(URL url, String forPath) throws IOException;

    /**
     * 递归获取指定路径下的所有资源列表
     *
     * @param path 资源路径
     * @return 所有资源
     * @throws IOException If I/O errors occur
     */
    public List<String> list(String path) throws IOException {
        List<String> names = new ArrayList<>();
        for (URL url : getResources(path)) {
            names.addAll(list(url, path));
        }
        return names;
    }
}
